
#include <stdio.h>
#include <stdlib.h>

#include <openssl/objects.h>
#include <openssl/evp.h>
#include <openssl/ec.h>
#include <openssl/hmac.h>
#include <string.h>
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/core_names.h>
#include <openssl/provider.h>

#include "abcrypto.h"

static int init_ctx(void **ctx, uint8_t *key, uint32_t klen, uint16_t alg)
{
	EVP_MD_CTX *mdctx;
	const EVP_MD *md = NULL;

	mdctx = EVP_MD_CTX_new();
	if (mdctx == NULL) {
		perror("Failed to create EVP context");
		return -1;
	}

	switch (alg & 0xf) {
	case ABCRYPTO_AUTH_ALG_MD5:
		md = EVP_md5();

		break;
	case ABCRYPTO_AUTH_ALG_SHA1:
		md = EVP_sha1();
		break;
	case ABCRYPTO_AUTH_ALG_SHA224:
		md = EVP_sha224();
		break;
	case ABCRYPTO_AUTH_ALG_SHA256:
		md = EVP_sha256();
		break;
	case ABCRYPTO_AUTH_ALG_SHA384:
		md = EVP_sha384();
		break;
	case ABCRYPTO_AUTH_ALG_SHA512:
		md = EVP_sha512();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_224:
		md = EVP_sha3_224();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_256:
		md = EVP_sha3_256();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_384:
		md = EVP_sha3_384();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_512:
		md = EVP_sha3_512();
		break;
	case ABCRYPTO_AUTH_ALG_SM3:
		md = EVP_sm3();
		break;
	default:
		break;
	}

	if (md == NULL) {
		EVP_MD_CTX_free(mdctx);
		perror("Failed to get SHA512 algorithm");
		return -1;
	}

	if (EVP_DigestInit_ex(mdctx, md, NULL) == 0) {
		EVP_MD_CTX_free(mdctx);

		perror("Failed to initialize digest");
		return -1;
	}

	*ctx = mdctx;

	return 0;
}

static void free_ctx(void *ctx)
{
	EVP_MD_CTX_free(ctx);
	return;
}

static int update(void *ctx, uint8_t *data, uint32_t len)
{
	return EVP_DigestUpdate(ctx, data, len);
}

static int finish(void *ctx, uint8_t *dgst)
{
	uint32_t len;
	return EVP_DigestFinal_ex(ctx, dgst, &len);
}

static int digest(uint8_t *data, uint32_t len, uint8_t *key, uint32_t klen,
		  uint8_t *dgst, uint16_t alg)
{
	unsigned int digest_len;

	const EVP_MD *md = NULL;

	switch (alg & 0xf) {
	case ABCRYPTO_AUTH_ALG_MD5:
		md = EVP_md5();

		break;
	case ABCRYPTO_AUTH_ALG_SHA1:
		md = EVP_sha1();
		break;
	case ABCRYPTO_AUTH_ALG_SHA224:
		md = EVP_sha224();
		break;
	case ABCRYPTO_AUTH_ALG_SHA256:
		md = EVP_sha256();
		break;
	case ABCRYPTO_AUTH_ALG_SHA384:
		md = EVP_sha384();
		break;
	case ABCRYPTO_AUTH_ALG_SHA512:
		md = EVP_sha512();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_224:
		md = EVP_sha3_224();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_256:
		md = EVP_sha3_256();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_384:
		md = EVP_sha3_384();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_512:
		md = EVP_sha3_512();
		break;
	case ABCRYPTO_AUTH_ALG_SM3:
		md = EVP_sm3();
		break;
	default:
		break;
	}

	return EVP_Digest(data, len, dgst, &digest_len, md, NULL) == 0;
}

static struct abcrypto_auth hash = {

	.init_ctx = init_ctx,
	.free_ctx = free_ctx,
	.update = update,
	.finish = finish,
	.digest = digest,
};

struct abcrypto_auth *openssl_hash(void)
{
	return &hash;
}

struct hmac_data {
	EVP_MAC_CTX *ctx;
	EVP_MAC *mac;
};

#if 0
static int hmac_init_ctx(void **ctx, uint8_t *key, uint32_t klen, uint16_t alg)
{
	struct hmac_data *data = NULL;

	EVP_MAC *mac = NULL;
	EVP_MAC_CTX *mac_ctx = NULL;
	OSSL_PARAM params[2];

	// 加载 HMAC 算法
	mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
	if (mac == NULL) {
		fprintf(stderr, "EVP_MAC_fetch failed\n");
		return -1;
	}

	// 创建 MAC 上下文
	mac_ctx = EVP_MAC_CTX_new(mac);
	if (ctx == NULL) {
		fprintf(stderr, "EVP_MAC_CTX_new failed\n");
		EVP_MAC_free(mac);
		return -1;
	}

	// 选择哈希算法
	switch (alg & 0xf) {
	case ABCRYPTO_AUTH_ALG_MD5:
		params[0] = OSSL_PARAM_construct_utf8_string(
			OSSL_MAC_PARAM_DIGEST, "MD5", 0);

		break;
	case ABCRYPTO_AUTH_ALG_SHA1:
		params[0] = OSSL_PARAM_construct_utf8_string(
			OSSL_MAC_PARAM_DIGEST, "SHA1", 0);

		break;
	case ABCRYPTO_AUTH_ALG_SHA224:
		params[0] = OSSL_PARAM_construct_utf8_string(
			OSSL_MAC_PARAM_DIGEST, "SHA224", 0);

		break;
	case ABCRYPTO_AUTH_ALG_SHA256:
		params[0] = OSSL_PARAM_construct_utf8_string(
			OSSL_MAC_PARAM_DIGEST, "SHA256", 0);

		break;
	case ABCRYPTO_AUTH_ALG_SHA384:
		params[0] = OSSL_PARAM_construct_utf8_string(
			OSSL_MAC_PARAM_DIGEST, "SHA384", 0);

		break;
	case ABCRYPTO_AUTH_ALG_SHA512:
		// 设置 HMAC 算法为 SHA512
		params[0] = OSSL_PARAM_construct_utf8_string(
			OSSL_MAC_PARAM_DIGEST, "SHA512", 0);

		break;
	case ABCRYPTO_AUTH_ALG_SHA3_224:
		params[0] = OSSL_PARAM_construct_utf8_string(
			OSSL_MAC_PARAM_DIGEST, "SHA3_224", 0);
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_256:
		params[0] = OSSL_PARAM_construct_utf8_string(
			OSSL_MAC_PARAM_DIGEST, "SHA3_256", 0);
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_384:
		params[0] = OSSL_PARAM_construct_utf8_string(
			OSSL_MAC_PARAM_DIGEST, "SHA3_384", 0);
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_512:
		params[0] = OSSL_PARAM_construct_utf8_string(
			OSSL_MAC_PARAM_DIGEST, "SHA3_512", 0);
		break;
	default:
		fprintf(stderr, "EVP_MAC_init failed\n");
		EVP_MAC_CTX_free(mac_ctx);
		EVP_MAC_free(mac);
		return -1;
	}

	params[1] = OSSL_PARAM_construct_end();
	if (EVP_MAC_init(mac_ctx, key, klen, params) != 1) {
		fprintf(stderr, "EVP_MAC_init failed\n");
		EVP_MAC_CTX_free(mac_ctx);
		EVP_MAC_free(mac);
		return -1;
	}

	data = malloc(sizeof(struct hmac_data));
	if (NULL == data) {
		fprintf(stderr, "Malloc failed\n");
		EVP_MAC_CTX_free(mac_ctx);
		EVP_MAC_free(mac);
		return -1;
	}

	data->mac = mac;
	data->ctx = mac_ctx;

	*ctx = data;

	return 0;
}

static void hmac_free_ctx(void *ctx)
{
	struct hmac_data *data = ctx;
	if (ctx == NULL)
		return;

	EVP_MAC_CTX_free(data->ctx);
	EVP_MAC_free(data->mac);
	return;
}

static int hmac_update(void *ctx, uint8_t *data, uint32_t len)
{
	struct hmac_data *hmac_data = ctx;

	if (EVP_MAC_update(hmac_data->ctx, data, len) != 1) {
		fprintf(stderr, "EVP_MAC_update failed\n");

		return -1;
	}

	return 0;
}

static int hmac_finish(void *ctx, uint8_t *dgst)
{
	unsigned int mac_len;
	struct hmac_data *hmac_data = ctx;
	if (EVP_MAC_final(hmac_data->ctx, dgst, (size_t *)&mac_len, 64) != 1) {
		// 处理最终计算失败的情况
		fprintf(stderr, "Failed to finalize MAC calculation\n");
		// EVP_MAC_free(mac);
		// EVP_MAC_CTX_free(ctx);
		return -1;
	}

	return 0;
}

static int hmac_digest(uint8_t *data, uint32_t len, uint8_t *key, uint32_t klen,
		       uint8_t *dgst, uint16_t alg)
{
	unsigned int hmac_len;

	// 选择哈希算法
	const EVP_MD *md = NULL;
	// 选择哈希算法
	switch (alg & 0xf) {
	case ABCRYPTO_AUTH_ALG_MD5:
		md = EVP_md5();

		break;
	case ABCRYPTO_AUTH_ALG_SHA1:
		md = EVP_sha1();
		break;
	case ABCRYPTO_AUTH_ALG_SHA224:
		md = EVP_sha224();
		break;
	case ABCRYPTO_AUTH_ALG_SHA256:
		md = EVP_sha256();
		break;
	case ABCRYPTO_AUTH_ALG_SHA384:
		md = EVP_sha384();
		break;
	case ABCRYPTO_AUTH_ALG_SHA512:
		md = EVP_sha512();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_224:
		md = EVP_sha3_224();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_256:
		md = EVP_sha3_256();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_384:
		md = EVP_sha3_384();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_512:
		md = EVP_sha3_512();
		break;
	default:
		break;
	}
	if (md == NULL) {
		fprintf(stderr, "Failed to get SHA512 algorithm\n");

		return -1;
	}

	return HMAC(md, key, klen, data, len, dgst, &hmac_len) == NULL;
}

#else

static int hmac_init_ctx(void **ctx, uint8_t *key, uint32_t klen, uint16_t alg)
{
	HMAC_CTX *hmac_ctx = HMAC_CTX_new();
	if (hmac_ctx == NULL) {
		fprintf(stderr, "Failed to create HMAC context\n");
		return -1;
	}

	// 选择哈希算法
	const EVP_MD *md = NULL;
	// 选择哈希算法
	switch (alg & 0xf) {
	case ABCRYPTO_AUTH_ALG_MD5:
		md = EVP_md5();

		break;
	case ABCRYPTO_AUTH_ALG_SHA1:
		md = EVP_sha1();
		break;
	case ABCRYPTO_AUTH_ALG_SHA224:
		md = EVP_sha224();
		break;
	case ABCRYPTO_AUTH_ALG_SHA256:
		md = EVP_sha256();
		break;
	case ABCRYPTO_AUTH_ALG_SHA384:
		md = EVP_sha384();
		break;
	case ABCRYPTO_AUTH_ALG_SHA512:
		md = EVP_sha512();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_224:
		md = EVP_sha3_224();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_256:
		md = EVP_sha3_256();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_384:
		md = EVP_sha3_384();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_512:
		md = EVP_sha3_512();
		break;
	case ABCRYPTO_AUTH_ALG_SM3:
		md = EVP_sm3();
		break;

	default:
		break;
	}
	if (md == NULL) {
		fprintf(stderr, "Failed to get SHA512 algorithm\n");

		return -1;
	}

	// 初始化 HMAC 计算
	if (HMAC_Init_ex(hmac_ctx, key, klen, md, NULL) != 1) {
		fprintf(stderr, "HMAC_Init_ex failed\n");
		HMAC_CTX_free(hmac_ctx);
		return 1;
	}

	*ctx = hmac_ctx;

	return 0;
}

static void hmac_free_ctx(void *ctx)
{
	HMAC_CTX_free(ctx);
	return;
}

static int hmac_update(void *ctx, uint8_t *data, uint32_t len)
{
	if (HMAC_Update(ctx, (unsigned char *)data, len) != 1) {
		fprintf(stderr, "HMAC_Update failed for part 1\n");
		HMAC_CTX_free(ctx);
		return 1;
	}

	return 0;
}

static int hmac_finish(void *ctx, uint8_t *dgst)
{
	unsigned int mac_len;

	if (HMAC_Final(ctx, dgst, &mac_len) != 1) {
		fprintf(stderr, "HMAC_Final failed\n");
		HMAC_CTX_free(ctx);
		return 1;
	}

	return 0;
}

static int hmac_digest(uint8_t *data, uint32_t len, uint8_t *key, uint32_t klen,
		       uint8_t *dgst, uint16_t alg)
{
	unsigned int hmac_len;

	// 选择哈希算法
	const EVP_MD *md = NULL;
	// 选择哈希算法
	switch (alg & 0xf) {
	case ABCRYPTO_AUTH_ALG_MD5:
		md = EVP_md5();

		break;
	case ABCRYPTO_AUTH_ALG_SHA1:
		md = EVP_sha1();
		break;
	case ABCRYPTO_AUTH_ALG_SHA224:
		md = EVP_sha224();
		break;
	case ABCRYPTO_AUTH_ALG_SHA256:
		md = EVP_sha256();
		break;
	case ABCRYPTO_AUTH_ALG_SHA384:
		md = EVP_sha384();
		break;
	case ABCRYPTO_AUTH_ALG_SHA512:
		md = EVP_sha512();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_224:
		md = EVP_sha3_224();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_256:
		md = EVP_sha3_256();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_384:
		md = EVP_sha3_384();
		break;
	case ABCRYPTO_AUTH_ALG_SHA3_512:
		md = EVP_sha3_512();
		break;
	case ABCRYPTO_AUTH_ALG_SM3:
		md = EVP_sm3();
		break;
	default:
		break;
	}
	if (md == NULL) {
		fprintf(stderr, "Failed to get SHA512 algorithm\n");

		return -1;
	}

	return HMAC(md, key, klen, data, len, dgst, &hmac_len) == NULL;
}

#endif

static struct abcrypto_auth hmac = {
	.init_ctx = hmac_init_ctx,
	.free_ctx = hmac_free_ctx,
	.update = hmac_update,
	.finish = hmac_finish,
	.digest = hmac_digest,
};

struct abcrypto_auth *openssl_hmac(void)
{
	return &hmac;
}